﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AmiEmuLib.AmigaMachine;
using System.Diagnostics;
using AmiEmuLib.AmigaMachine.AmigaMemory;
using System.Xml;
using System.Globalization;
using TomanuExtensions;

namespace AmiEmuLib.SaveState
{
    /// <summary>
    /// Part of memory bank. Continous memory range filled with same cells.
    /// In diff mode there can be space between ranges.
    /// </summary>
    [DebuggerDisplay("Fill, {BankType}, Address: {Address}, Size = {Size}, Patterm = {\"0x\" + Pattern.ToString(\"X4\")}")]
    public class FillMemoryRange : MemoryRange
    {
        /// <summary>
        /// Fill pattern.
        /// </summary>
        private uint m_pattern = uint.MaxValue;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_reader"></param>
        /// <param name="a_bank_state"></param>
        internal FillMemoryRange(XmlReader a_reader, MemoryBankState a_bank_state)
            : base(a_reader, a_bank_state)
        {
            Pattern = a_reader.GetAttributeHexUShort("Pattern");

            a_reader.MoveToNextElement("Fill");
        }        

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_bank_state"></param>
        /// <param name="a_address"></param>
        /// <param name="a_size"></param>
        internal FillMemoryRange(MemoryBankState a_bank_state, uint a_address, MemorySize a_size) 
            : base(a_bank_state, a_address, a_size)
        {
        }

        /// <summary>
        /// Fill pattern.
        /// </summary>
        public ushort Pattern
        {
            get
            {
                if (m_pattern == uint.MaxValue)
                    throw new InvalidOperationException("Load or persists first.");

                return (ushort)m_pattern;
            }
            private set
            {
                m_pattern = value;
            }
        }

        internal override void Restore(Memory a_memory)
        {
            a_memory.FillWords(Address, Size, Pattern);
        }

        internal override void Persist(MemoryBank a_bank)
        {
            Pattern = a_bank.GetWord(Address);
        }

        internal override void WriteXml(XmlWriter a_writer)
        {
            base.WriteXml(a_writer);

            a_writer.WriteHexAttribute("Pattern", Pattern);
        }

        public override bool Equals(object a_obj)
        {
            if (a_obj == null)
                return false;
            FillMemoryRange range = a_obj as FillMemoryRange;
            if (range == null)
                return false;

            return (range.Address == Address) && (range.Size == Size) &&
                   (range.Pattern == Pattern);
        }

        public override int GetHashCode()
        {
            return Address.GetHashCode() ^ Size.GetHashCode() ^ Pattern.GetHashCode();
        }

        internal override uint GetCharSize()
        {
            return FillMemoryRange.GetCharSize(Size);
        }

        internal override ushort GetWord(uint a_address)
        {
            if (!InRange(a_address))
                throw new ArgumentOutOfRangeException();
            if (!MemoryHelpers.IsWordAllign(a_address))
                throw new ArgumentException();

            return Pattern;
        }

        /// <summary>
        /// Numbers of char (with end lines and indentation spaces) of xml content.
        /// </summary>
        /// <param name="a_size"></param>
        /// <returns></returns>
        internal static  uint GetCharSize(uint a_size)
        {
            if (a_size == 0)
                return 0;

            //0000000000111111111222222222233333333334444444444555555555556666666666777777777
            //1234567890123456789012345678901234567890123456789012345678901234567890123456789
            //      <Fill Address="0x00000000" Size="" Pattern="0xFFFF" />
            return 61 + ((uint)Math.Log10(a_size) + 1);
        }
    }
}
